package com.process.on;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import java.io.Console;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.*;

/**
 * 代码运行前记得编译 否则找不到users.json文件
 * 可以mvn clean compile
 * 可以直接idea编译
 *
 * @author demo
 * @date 2020/12/28
 */
public class ProcessExpansionApplication {
    /**
     * 最大线程数 剩余空闲文件数 > 执行线程数
     */
    final static int MAX_CORE_THREAD_SIZE = 10;

    /**
     * 有效文件数量提示 文案
     */
    final static String EFFECTIVE_NUMS_MSG = "您的剩余可用文件数为[%d]，不足[%d]个，不能达到程序最佳性能，此次扩容文件数为[%d]个！！!\r\n";


    /**
     * 代码运行前记得编译 否则找不到users.json文件
     * 可以mvn clean compile
     * 可以直接idea编译
     * <p>
     * 入口 可以配置用户的账号密码
     *
     * @param args 入口
     * @throws IOException 异常
     */
    public static void main(String[] args) throws IOException {
        //输入用户
        User user = inUser();
        //配置帮助者
        List<User> helpers = inHelpers();
        //设置帮助者
        try {
            user.setHelpers(helpers);
        } catch (RuntimeException e) {
            System.exit(0);
        }

        //定义响应字段
        do {
            //获取扩容文件数 判断有效扩容文件数
            int expandSize = getEffectiveNums(user, initExpansionSize());
            if (expandSize <= 0) {
                System.out.printf("操作可用文件数为[%d]，请确认您输入的扩容文件数 或 清理processOn里的流程图后重试！！！\r\n"
                        , expandSize);
                continue;
            }

            long startTime = System.currentTimeMillis();
            System.out.println("开始扩容...");
            //扩容操作
            CountDownLatch countDownLatch = new CountDownLatch(expandSize);
            ThreadPoolExecutor threadPoolExecutor =
                    new ThreadPoolExecutor(MAX_CORE_THREAD_SIZE,
                            MAX_CORE_THREAD_SIZE,
                            10,
                            TimeUnit.SECONDS,
                            new LinkedBlockingQueue<>(expandSize),
                            (ThreadFactory) Thread::new);
            System.out.println("完成线程池创建..");
            for (int i = 0; i < expandSize; i++) {
                //提交线程池
                threadPoolExecutor.submit(() -> {
                    user.expand();
                    countDownLatch.countDown();
                });
            }
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                //中断异常
                e.printStackTrace();
            }
            System.out.println(user.getMaxFreeChart());
            threadPoolExecutor.shutdown();
            System.out.println("结束扩容...");
            System.out.printf("扩容持续时间:%d ms\r\n", System.currentTimeMillis() - startTime);
        } while (continueLoop());
        //登出
        user.loginOut();
    }

    /**
     * 获取有效扩展文件数
     *
     * @param user       操作用户
     * @param expandSize 扩展文件数
     * @return 需要扩展的文件数
     */
    private static int getEffectiveNums(User user, int expandSize) {
        String result = user.getMaxFreeChart();
        final String fileCountCode = "filecount";
        final String totalCountCode = "totalcount";
        JSONObject resultBody = JSONObject.parseObject(result);
        Integer fileCount = resultBody.getInteger(fileCountCode);
        Integer totalCount = resultBody.getInteger(totalCountCode);
        //剩余有效文件 < 并发线程时
        if (totalCount - fileCount < MAX_CORE_THREAD_SIZE) {
            expandSize = totalCount - fileCount;
            System.out.printf("processOn剩余可使用文件数[%d]个\r\n", expandSize);
            System.out.printf(EFFECTIVE_NUMS_MSG, expandSize, MAX_CORE_THREAD_SIZE, expandSize);
        }
        return expandSize;
    }

    /**
     * 判断继续扩容
     *
     * @return 是否需要继续扩容
     */
    private static boolean continueLoop() {
        Console c = System.console();
        while (true) {
            String bool = c.readLine("是否还需要扩容(y/n): ");
            if (null == bool) {
                bool = "";
            }
            if ("Y".equals(bool.toUpperCase())) {
                return true;
            } else if ("N".equals(bool.toUpperCase())) {
                return false;
            } else {
                System.out.println("输入有误！！！");
            }
        }
    }

    /**
     * 初始化扩容文件数
     */
    private static int initExpansionSize() {
        Console c = System.console();
        while (true) {
            //输入扩容文件数
            String nums = c.readLine("请输入需要扩容的文件数：");
            try {
                int expansionNums = Integer.parseInt(nums);
                System.out.printf("扩容 %d 个免费文件...\r\n", expansionNums);
                return expansionNums;
            } catch (Exception e) {
                System.out.println("扩容文件数输入有误请重新输入！！！");
            }
        }
    }

    /**
     * 配置帮助者
     *
     * @return 帮助者
     * @throws IOException 一场
     */
    private static List<User> inHelpers() throws IOException {
        List<User> helpers;
        Console c = System.console();
        while (true) {
            String url = c.readLine("请复制输入users.json的绝对路径: ");
            File file = new File(url);
            if (!file.exists()) {
                System.out.printf("%s 路径下没有文件，请重新输入！！！\r\n", url);
                continue;
            }
            //获取user.json  这里偷懒直接用fastjson读了
            String json = JSON.toJSONString(JSONObject.parseObject(new FileInputStream(file), Object.class));
            helpers = JSONArray.parseArray(json, User.class);
            break;
        }
        return helpers;
    }

    /**
     * 输入用户
     *
     * @return 操作用户
     */
    private static User inUser() {
        User user;
        Console c = System.console();
        while (true) {
            String name = c.readLine("您的账号: ");
            char[] password = c.readPassword("您的密码: ");

            try {
                //用户processOn账号密码 需要自己配置
                user = new User(name, new String(password));
            } catch (RuntimeException e) {
                System.out.println("账号密码有误请重新输入！！！");
                continue;
            }
            break;
        }
        return user;
    }
}
